package com.github.niefy.modules.openapi.server.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.github.niefy.common.exception.ErrorMessage;
import com.github.niefy.common.utils.EhCacheUtils;
import com.github.niefy.common.utils.R;
import com.github.niefy.modules.openapi.server.dto.in.AppAccessTokenIn_001;
import com.github.niefy.modules.third.entity.ThirdSysInfo;
import com.github.niefy.modules.third.service.ThirdSysInfoService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.api.WxMpService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * openapi服务端令牌处理
 */
@RestController
@RequestMapping("/openapi/accessToken")
@Tag(name = "openapi服务端令牌处理")
@RequiredArgsConstructor
@Slf4j
public class AccessController {

    private final WxMpService wxMpService;
    private final ThirdSysInfoService thirdSysInfoService;

    /**
     * 1、检查appid是否有效
     * 2、检查缓存中是否存在正在生效的token
     * 3、校验请求签名
     * 4、检查请求时间戳是否大于缓存中的前次时间戳，大于继续，不大于请求无效
     * 5、若oldToken存在且非强制刷新请求，将oldToken返回
     * 6、若oldToken不存在或强制刷新请求，生成新token，缓存新token及新时间戳
     */
    @PostMapping("/stableToken")
    @Operation(summary = "颁发稳定无特殊限制access_token",description = "")
    public R getStableToken(HttpServletRequest request, HttpServletResponse response,
                            @RequestBody @Validated AppAccessTokenIn_001 in) throws WxErrorException {
        String appAccessTokenOld = null;
        //1、检查appid是否有效
        var thirdSysInfo = thirdSysInfoService.lambdaQuery()
                .eq(ThirdSysInfo::getOpenAppid, in.getAppid())
                .one();
        if (BeanUtil.isEmpty(thirdSysInfo)){
            return ErrorMessage.messageToR("A0313");
        }
        //2、检查缓存中是否存在正在生效的token
        var appAccessTokenOldElement = EhCacheUtils.getNativeElement(in.getAppid(), "appAccessServerTokenCache");
        var oldTokenExist = BeanUtil.isNotEmpty(appAccessTokenOldElement);
        if (oldTokenExist){
            appAccessTokenOld = (String) appAccessTokenOldElement.getObjectValue();
        }
        /*
        var s = aes.encryptHex(String.valueOf(System.currentTimeMillis()));
        var s1 = aes.decryptStr(s);*/
        //3、校验请求签名
        //签名为使用md5摘要的（appid+timestamp+secret）
        var signatureCheck = SecureUtil.md5(in.getAppid()+in.getTimestamp().toString()+ thirdSysInfo.getServerSecret());
        if (!StrUtil.equals(in.getSignature(), signatureCheck)){
            return ErrorMessage.messageToR("A0314");
        }
        //4、若前次时间戳存在，检查请求时间戳是否大于缓存中的前次时间戳，大于继续，不大于请求无效
        var timestamp = in.getTimestamp();
        Long lastTimestamp = EhCacheUtils.get("access_token-" + in.getAppid(), "eternalCache");
        if (BeanUtil.isNotEmpty(lastTimestamp) && timestamp<=lastTimestamp){
            return ErrorMessage.messageToR("A0314");
        }
        //5、若oldToken存在且非强制刷新请求，将oldToken返回
        //6、若oldToken不存在或强制刷新请求，生成新token，缓存新token及新时间戳
        //强制刷新token
        if (!oldTokenExist || BooleanUtil.isTrue(in.getForceRefresh())){
            var tokenStr = RandomUtil.randomString(32);
            EhCacheUtils.put(in.getAppid(), tokenStr, "appAccessServerTokenCache");

            //无论是否刷新，更新请求时间戳
            EhCacheUtils.put("access_token-" + in.getAppid(), timestamp, "eternalCache");
            return R.ok().put("accessToken",tokenStr)
                    .put("expiresIn", 86_400_000);
        }
        //无论是否刷新，更新请求时间戳
        EhCacheUtils.put("access_token-" + in.getAppid(), timestamp, "eternalCache");
        //有效时间等于元素创建时间/毫秒+预设缓存时间/秒-当前系统时间/毫秒
        var expiresIn = appAccessTokenOldElement.getCreationTime() + appAccessTokenOldElement.getTimeToLive() * 1000L - System.currentTimeMillis();
        return R.ok().put("accessToken", appAccessTokenOld)
                .put("expiresIn", expiresIn);
    }

    /*@GetMapping("/onceAccessToken")
    @Operation(summary = "获取一次性access_token",description = "")
    public R getOnceAccessToken(HttpServletRequest request, HttpServletResponse response) throws WxErrorException {

        return R.ok();
    }*/
}
